<?php

class Logger {

    /**
     * 错误级别
     */
    const LOGGER_LEVEL_TRACE = 1;
    const LOGGER_LEVEL_DEBUG = 2;
    const LOGGER_LEVEL_WARNING = 3;
    const LOGGER_LEVEL_ERROR = 4;
    const LOGGER_LEVEL_FATAL = 5;

    /**
     * crlf
     * 换行符
     *
     * @var string
     */
    public static $crlf = PHP_EOL;

    /**
     * 日志配置
     *
     * @returns
     */
    public static $conf = array();

    /**
     * writeln
     *
     * @param  mixed $fpath
     * @param  mixed $message
     * @access public
     * @return void
     */
    public static function writeln($fpath, $message) {

        // 配置静态载入
        if (!self::$conf) {
            self::$conf = Yaf_Registry::get("global_config")->get('logger')->toArray();
        }

        $dir = self::$conf['output_dir'];
        $dir = rtrim($dir, '/') . '/' . ltrim($fpath, '/');

        // 自动创建不存在的目录
        $directory = dirname($dir);
        if (!is_dir($directory)) {
            $oldUmask = umask(0);
            mkdir($directory, 0777, true);
            umask($oldUmask);
        }

        $fd = fopen($dir, "a+");
        if (!$fd) {
            error_log("Logger: Cannot open file ($dir)");
            return false;
        }
        fwrite($fd, "[" . date('Y-m-d H:i:s') . "]\t" . $message . self::$crlf);
        fclose($fd);
    }

    /**
     * debug
     *
     * @param  mixed $message
     * @access public
     * @return void
     */
    public static function debug($message, $noReport = true){
        return Logger::log($message, self::LOGGER_LEVEL_DEBUG, $noReport);
    }

    /**
     * warning
     *
     * @param  mixed $message
     * @access public
     * @return void
     */
    public static function warning($message){
        return Logger::log($message, self::LOGGER_LEVEL_WARNING);
    }

    /**
     * trace
     *
     * @param  mixed $message
     * @access public
     * @return void
     */
    public static function trace($message, $noReport = false){
        return Logger::log($message, self::LOGGER_LEVEL_TRACE, $noReport);
    }

    /**
     * fatal
     *
     * @param  mixed $message
     * @access public
     * @return void
     */
    public static function fatal($message){
        return Logger::log($message, self::LOGGER_LEVEL_FATAL);
    }

    /**
     * error
     *
     * @param  string $message
     * @access public
     * @return void
     */
    public static function error($message){
        return Logger::log($message, self::LOGGER_LEVEL_ERROR);
    }

    /**
     * log
     *
     * @param  mixed $message
     * @param  mixed $level
     * @access public
     * @return void
     */
    public static function log($message, $level, $noReport = false){

        // 配置静态载入
        if (!self::$conf || !is_array(self::$conf)) {
            self::$conf = Yaf_Registry::get("global_config")->get('logger')->toArray();
        }

        if ($message instanceof Exception) {
            // 异常记录Trace日志
            $logTrace = '';
            $exceptionTrace = reset($message->getTrace());
            if ($exceptionTrace) {
                $logTrace = sprintf('[line: %s, %s%s%s] ', $exceptionTrace['line'], $exceptionTrace['class'], $exceptionTrace['type'], $exceptionTrace['function']);
            }
            $message = $logTrace . $message->getMessage() . '[' .$message->getCode(). ']';
        }

        $backtrace = debug_backtrace();
        array_shift($backtrace); // myself
        array_shift($backtrace); //
        $traceinfo = array_shift($backtrace);
        $traceinfo['className'] = $traceinfo['class'];

        $conf = self::$conf[$traceinfo['className']];
        $conf['level']  = $conf["level"] ? $conf["level"] : self::$conf['level'];
        $conf['output'] = $conf["output"] ? $conf["output"] : self::$conf['output'];
        $conf['output_dir'] = $conf["output_dir"] ? $conf["output_dir"] : self::$conf['output_dir'];
        $conf['use_arg'] = $conf["use_arg"] ? $conf["use_arg"] : self::$conf['use_arg'];

        switch($level) {
            case self::LOGGER_LEVEL_TRACE:
                $myLevel = 'TRACE';
                break;
            case self::LOGGER_LEVEL_DEBUG:
                $myLevel = 'DEBUG';
                break;
            case self::LOGGER_LEVEL_WARNING:
                $myLevel = 'WARNING';
                break;
            case self::LOGGER_LEVEL_ERROR:
                $myLevel = 'ERROR';
                break;
            case self::LOGGER_LEVEL_FATAL:
                $myLevel = 'FATAL';
                break;
            default:
                $myLevel = 'N/A';
        }

        if ($conf['level'] <= $level){
            if ($traceinfo['className']){
                $out = sprintf ("[%s] [%s::%s %d] [%s] {%s} %s" . self::$crlf,
                    date('Y-m-d H:i:s'), $traceinfo['className'], $traceinfo['function'],  $traceinfo['line'], $myLevel, $traceToken, $message);
                if ($conf['output'] == 'stdout') {
                    echo $out;
                } else if ($conf['output'] == 'file') {
                    $outfile = "$conf[output_dir]/$traceinfo[className]";
                    $arg1 = trim($_SERVER['argv'][1]);
                    if ($conf['use_arg'] && $arg1 && !$_SERVER['SERVER_SOFTWARE']) { // fix: 浏览器有get参数时创建怪异的文件名
                        $outfile .=  "_$arg1";
                    }
                    $outfile .= '.' . date('Y-m-d') . '.log';
                    if ($fd = @fopen($outfile, 'a+')) {
                        fwrite($fd, $out);
                        fclose($fd);
                    } else {
                        error_log("Logger: Cannot open file ($outfile)");
                    }
                } else {
                    error_log("Logger: unrecognized output type ($conf[output])");
                }
            } else if ($traceinfo['function']){
                printf ("[%s] [%s %d] [%s] {%s} %s" . self::$crlf, date('Y-m-d H:i:s'), $traceinfo['function'],  $traceinfo['line'], $traceToken, $message);
            } else {
                printf ("[%s] [main] {%s} %s" . self::$crlf, date('Y-m-d H:i:s'), $traceToken, $message);
            }
        }

        if (!$noReport) {
            // 上报到DC
            $reportData = array(
                'traceToken' => $traceToken,
                'level' => $myLevel,
                'message' => $message,
                'function' => $traceinfo['function'],
                'className' => $traceinfo['className'],
                'line' => $traceinfo['line'],
                'extInt1' => 0,
                'extInt2' => 0,
                'extInt3' => 0,
                'extInt4' => 0,
                'extStr1' => '',
                'extStr2' => '',
                'extStr3' => '',
                'extStr4' => '',
            );
            // http://m.isd.com/itil/cms/cdp/applyMonitorData.php?dataId=1000240
            Logger::report(1000240, $reportData);
        }

        return $outfile;
    }

    /**
     * 上报到 dc
     *
     * @returns
     */
    public static function report($typeId, $data) {

        $logName = 'cdp' . $typeId;
        $data['ip'] = Util_LocalIP::getLocalIP();

        // 过滤制表符，中文
        // $data['message'] = str_replace(array("\n", "\n\r", "\t"), '|', $data['message']);
        $data['message'] = urlencode($data['message']);

        try {
            Tencent_Dc::writeLog($logName, array(
                'typeid' => intval($typeId),
                'time' => time(),
                'traceToken' => substr('token:' . $data['traceToken'], 0, 31),
                'level' => substr($data['level'], 0, 15),
                'message' => substr($data['message'], 0, 1000),
                'function' => substr($data['function'], 0, 50),
                'className' => substr($data['className'], 0, 50),
                'line' => $data['line'],
                'ip' => $data['ip'],
                'extInt1' => $data['extInt1'],
                'extInt2' => $data['extInt2'],
                'extInt3' => $data['extInt3'],
                'extInt4' => $data['extInt4'],
                'extStr1' => substr(urlencode($data['extStr1']), 0, 512),
                'extStr2' => substr(urlencode($data['extStr2']), 0, 512),
                'extStr3' => substr(urlencode($data['extStr3']), 0, 512),
                'extStr4' => substr(urlencode($data['extStr4']), 0, 512),
            ));
        } catch (Exception $e) {
            $filename = '/tmp/log_dc' . date('Y-m-d') . 'error.log';
            file_put_contents($filename, $e->getMessage(), FILE_APPEND);
            // 避免死循环，这里不使用 Logger::error 记录日志
        }

        return true;
    }
}
